package org.dom4j.tree;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.dom4j.Attribute;
import org.dom4j.CharacterData;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;

public class DefaultElement implements Element {

  String name;
  List attributes;
  List content;
  Node parent;

  DefaultElement(Node parent, String name) {
    this.parent = parent;
    this.name = name;
  }
  
  public DefaultElement(String name) {
    this(null, name);
  }
  
  private class ElementList extends ArrayList {

    private static final long serialVersionUID = -3986456486379996528L;

    public void clear() {
      super.clear();
      content.clear();
    }
    
    private void addContent(Object element) {
      super.add(element);
    }
    
    public boolean add(Object element) {
      super.add(element);
      if (element instanceof Node) {
        ((Node)element).setParent(DefaultElement.this);
        DefaultElement.this.addContentToList((Node)element);
      }
      return true;
    }
    
    public void add(int index, Object element) {
      super.add(index, element);
      if (element instanceof Node) {
        ((Node)element).setParent(DefaultElement.this);
        DefaultElement.this.addContentToList(index, (Node)element);
      }
    }
  }
  
  
  public List elements() {
    // TODO: BIND THIS LIST TO OBJECT TO MANAGE CLEAR CALL
    ElementList list = new ElementList();
    if (content != null) {
      for (int i = 0; i < content.size(); i++) {
        if (content.get(i) instanceof Element) {
          list.addContent(content.get(i));
        }
      }
    }
    return list;
  }

  public List elements(String name) {
    // TODO: BIND THIS LIST TO OBJECT TO MANAGE CLEAR CALL
    if (content == null) {
      return new ArrayList();
    }
    ArrayList list = new ArrayList() {
      public void clear() {
        for (int i = content.size() - 1; i >= 0; i--) {
          for (int j = 0; j < size(); j++) {          
            if (content.get(i) == get(j)) {
              content.remove(i);
            }
          }
        }
        super.clear();
      }
    };
    for (int i = 0; i < content.size(); i++) {
      if (content.get(i) instanceof Element) {
        Element element = (Element)content.get(i);
        if (name.equals("*") || element.getName().equals(name)) {
          list.add(content.get(i));
        }
      }
    }
    return list;
  }

  public String getName() {
    return name;
  }

  public Element element(String name) {
    if (content == null) {
      return null;
    }
    for (int i = 0; i < content.size(); i++) {
      if (content.get(i) instanceof Element) {
        Element element = (Element)content.get(i);
        if (element.getName().equals(name)) {
          return element;
        }
      }
    }
    return null;
  }

  public String getPath() {
    // TODO Auto-generated method stub
    return null;
  }

  public Document getDocument() {
    if (parent instanceof Document) {
      return (Document)parent;
    } else if (parent != null) {
      return getParent().getDocument();
    } else {
      return null;
    }
  }

  public Element getParent() {
    if (parent instanceof Element) {
      return (Element)parent;
    }
    return null;
  }

  public Element addElement(String name) {
    DefaultElement child = new DefaultElement(this, name);
    addContentToList(child);
    return child;
  }

  public void setName(String name) {
    this.name = name;
    
  }

  public String attributeValue(String attribName) {
    if (attributes != null) {
      for (int i = 0; i < attributes.size(); i++) {
        Attribute attrib = (Attribute)attributes.get(i);
        if (attrib.getName().equals(attribName)) {
          return attrib.getValue();
        }
      }
    }
    return null;
  }

  public Element addAttribute(String name, String value) {
    // first see if attribute exists already
    DefaultAttribute attribute = (DefaultAttribute)attribute(name);
    if (attribute != null) {
      attribute.setValue(value);
      return this;
    }
    DefaultAttribute defaultAttribute = new DefaultAttribute(this, name, value);
    if (attributes == null) {
      attributes = new ArrayList();
    }
    attributes.add(defaultAttribute);
    return this;
  }

  public String valueOf(String xpath) {
    // TODO Auto-generated method stub
    return null;
  }
  
  public void setParent(Element parent) {
   this.parent = parent; 
  }
  void setDocument(Document parent) {
    this.parent = parent;
  }

  public void add(Element element) {
    element.setParent(this);
    addContentToList(element);
  }
  
  private void addContentToList(int index, Node item) {
    if (content == null) {
      content = new ArrayList();
    }
    content.add(index, item);
  }
  
  private void addContentToList(Node item) {
    if (content == null) {
      content = new ArrayList();
    }
    content.add(item);
  }

  private void addAttributeToList(Attribute attribute) {
    if (attributes == null) {
      attributes = new ArrayList();
    }
    attributes.add(attribute);
  }
  
  public void add(Node element) {
    if (element instanceof Element ||
        element instanceof CharacterData) {
      element.setParent(this);
      addContentToList(element);
    } else if (element instanceof Attribute) {
      element.setParent(this);
      addAttributeToList((Attribute)element);
    } else {
      throw new UnsupportedOperationException();
    }
  }

  public Node selectSingleNode(String xpath) {
    System.out.println("Element.selectSingleNode XPATH = " + xpath);
    List list = selectNodes(xpath);
    if (list.size() > 0) {
      return (Node)list.get(0);
    }
    return null;
  }

  public List selectNodes(String xpath) {
    // support "inputs/*"
    // support "
    // TODO Auto-generated method stub
    // ("/" + ACTION_SEQUENCE + "/" + ACTIONS_NAME
    // support standard traversal
    System.out.println("Element.selectNodes XPATH = " + xpath);
    // This does not support advanced stuff yet
    String paths[] = xpath.split("/");
    int start = 0;
    if (paths.length > 0 && paths[0].equals("")) {
      start = 1;
    }
    List allElements = new ArrayList();
    allElements.add(this);

    for (int i = start; i < paths.length; i++) {
      List foundElements = new ArrayList();
      for (int j = 0; j < allElements.size(); j++) {
        Element currElement = (Element)allElements.get(j);
        foundElements.addAll(currElement.elements(paths[i]));
      }
      allElements = foundElements;
    }
    return allElements;
  }

  public String getText() {
    if (content != null) {
      for (int i = 0; i < content.size(); i++) {
        if (content.get(i) instanceof CharacterData) {
          return ((CharacterData)content.get(i)).getText();
        }
      }
    }
    return "";
  }

  public void setText(String text) {
    if (content != null) {
      Iterator iter = content.iterator();
      while (iter.hasNext()) {
        Object obj = iter.next();
        if (obj instanceof CharacterData) {
          iter.remove();
        }
      }
    }
    CharacterData data = new DefaultText(this, text);
    addContentToList(data);
  }

  protected String asXML(String indent) {
    String str = indent + "<" + name;
    if (attributes != null && attributes.size() >= 0) {
      for (int i = 0; i < attributes.size(); i++) {
        Attribute attrib = (Attribute)attributes.get(i);
        str += " " + attrib.asXML();
      }
    }
    boolean newlineAdded = false;
    if ((content != null && content.size() >= 0)) {
      str += ">";
       for (int i = 0; i < content.size(); i++) {
         Node contentItem = (Node)content.get(i);
         if (contentItem instanceof DefaultElement) {
           if (i == 0) {
             newlineAdded = true;
             str += "\n";
           }
           str += ((DefaultElement)contentItem).asXML(indent + "  ");
         } else {
           str += contentItem.asXML();
         }
       }
       if (newlineAdded) {
         str += indent;
       }
       str += "</" + name + ">\n";
    } else {
      str += "/>\n";
    }
    return str;
    
  }
  
  public String asXML() {
    return asXML("");
  }

  public void clearContent() {
    if (content != null) {
      content.clear();
    }
  }

  public Element addCDATA(String data) {
    DefaultCDATA cdata = new DefaultCDATA(this, data);
    addContentToList(cdata);
    return this;    

  }

  public boolean remove(Attribute attrib) {
    if (attributes != null) {
      return attributes.remove(attrib);
    }
    return false;
  }
  
  public boolean remove(Element elem) {
    // TODO Auto-generated method stub
    return false;
  }
  
  public boolean remove(CharacterData cdata) {
    //  TODO Auto-generated method stub
    return false;
  }

  public Attribute attribute(String attribName) {
    if (attributes != null) {
      for (int i = 0; i < attributes.size(); i++) {
        Attribute attrib = (Attribute)attributes.get(i);
        if (attrib.getName().equals(attribName)) {
          return attrib;
        }
      }
    }
    return null;
  }
  
  public Node detach() {
    if (parent instanceof DefaultElement) {
      ((DefaultElement)parent).content.remove(this);
    } else if (parent instanceof DefaultDocument) {
      ((DefaultDocument)parent).rootElement = null;
    }
    parent = null;
    return this;
  }

}
